package com.hua.customviews.widget

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.graphics.Region
import android.os.Build
import android.util.AttributeSet
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.hua.customviews.R

@Suppress("unused", "MemberVisibilityCanBePrivate")
class IndicatorConstraintLayout(
    private val mContext: Context,
    private val attrs: AttributeSet?,
    defStyleAttr: Int
) : ConstraintLayout(mContext, attrs, defStyleAttr) {
    init {
        initView()
    }

    constructor(mContext: Context, attrs: AttributeSet?) : this(mContext, attrs, 0) {
        initView()
    }

    constructor(mContext: Context) : this(mContext, null, 0) {
        initView()
    }

    private val paint = Paint()
    private val path = Path()
    private var isInvalidate = true
    private var invalidateCount = 0

    //允许的绘制次数 绘制次数越多性能消耗越严重
    var drawCount = 1

    //指示器宽度
    var indicatorWidth = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //指示器高度
    var indicatorHeight = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //指示器偏移量
    var indicatorOffset = 0.5f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //指示器margin
    var indicatorMarginStart = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    var indicatorMarginEnd = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //指示器依附的边
    var indicatorAttachTo = Companion.top
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //指示器（整体颜色）
    var indicatorColor: Int = 0xffffff
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //形状风格 填充/镂空
    var indicatorStyle = fill
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //镂空时的边框粗细
    var borderWidth = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //圆角
    var borderRadius = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    var borderRadiusTopStart = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
        get() = if (borderRadiusLimit >= field) field else borderRadiusLimit
    var borderRadiusTopEnd = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
        get() = if (borderRadiusLimit >= field) field else borderRadiusLimit
    var borderRadiusBottomStart = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
        get() = if (borderRadiusLimit >= field) field else borderRadiusLimit
    var borderRadiusBottomEnd = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
        get() = if (borderRadiusLimit >= field) field else borderRadiusLimit
    private var borderRadiusLimit = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //阴影颜色
    var shadowColor: Int = Color.parseColor("#00FFFFFF")
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //阴影偏移
    var shadowDx = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    var shadowDy = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //阴影半径
    var shadowRadius = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //内部计算用宽高
    private var shapeWidth = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    private var shapeHeight = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //形状Padding
    var shapePadding = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    var shapePaddingTop = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    var shapePaddingStart = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    var shapePaddingEnd = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }
    var shapePaddingBottom = 0f
        set(value) {
            if (field != value) isInvalidate = true
            field = value
        }

    //伴生标志
    companion object {
        //方向
        const val top = 0
        const val start = 1
        const val bottom = 2
        const val end = 3

        //填充风格
        const val fill = 0
        const val stroke = 1
        const val fillStroke = 2
    }

    //当设置有listener时，采用外部绘制图形
    private var onDrawListener: ((path: Path, view: View) -> Unit)? = null

    /**
     * 自定义背景形状
     * path:用于绘制
     * view:this实例
     * path最后需要close()
     */
    fun setOnDrawListener(onDrawListener: (path: Path, view: View) -> Unit) {
        this.onDrawListener = onDrawListener
    }

    //指示器绘制 当设置有listener时，采用外部绘制
    private var onDrawIndicatorListener: ((path: Path, view: View) -> Unit)? = null

    /**
     * 自定义指示器
     * path:用于绘制
     * view:this实例
     * path最后需要close()
     */
    fun setOnDrawIndicatorListener(onDrawIndicatorListener: (path: Path, view: View) -> Unit) {
        this.onDrawIndicatorListener = onDrawIndicatorListener
    }

    private fun initView() {
        //防止不绘制
        if (childCount == 0) setWillNotDraw(false)
        //如果是安卓9以下设备则设置软件绘制 关闭硬件加速
        if (!isInEditMode) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) setLayerType(
            View.LAYER_TYPE_SOFTWARE,
            null
        )
        //读取xml属性
        val typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.IndicatorConstraintLayout)
        typedArray.apply {
            indicatorWidth =
                getDimension(R.styleable.IndicatorConstraintLayout_indicatorWidth, indicatorWidth)
            indicatorHeight =
                getDimension(R.styleable.IndicatorConstraintLayout_indicatorHeight, indicatorHeight)
            indicatorOffset = getFloat(R.styleable.IndicatorConstraintLayout_indicatorOffset, indicatorOffset)
            indicatorMarginStart = getDimension(
                R.styleable.IndicatorConstraintLayout_indicator_marginStart,
                indicatorMarginStart
            )
            indicatorMarginEnd =
                getDimension(R.styleable.IndicatorConstraintLayout_indicator_marginEnd, indicatorMarginEnd)
            indicatorAttachTo = getInteger(R.styleable.IndicatorConstraintLayout_attachTo, Companion.top)
            indicatorColor = getColor(R.styleable.IndicatorConstraintLayout_indicatorColor, indicatorColor)
            indicatorStyle = getInteger(R.styleable.IndicatorConstraintLayout_indicatorStyle, indicatorStyle)
            borderWidth =
                getDimension(R.styleable.IndicatorConstraintLayout_indicatorBorderWidth, borderWidth)
            borderRadius = getDimension(R.styleable.IndicatorConstraintLayout_borderRadius, borderRadius)
            borderRadiusTopStart =
                getDimension(R.styleable.IndicatorConstraintLayout_borderRadius_topStart, borderRadius)
            borderRadiusTopEnd =
                getDimension(R.styleable.IndicatorConstraintLayout_borderRadius_topEnd, borderRadius)
            borderRadiusBottomStart =
                getDimension(R.styleable.IndicatorConstraintLayout_borderRadius_bottomStart, borderRadius)
            borderRadiusBottomEnd =
                getDimension(R.styleable.IndicatorConstraintLayout_borderRadius_bottomEnd, borderRadius)
            shadowColor = getColor(R.styleable.IndicatorConstraintLayout_shadowColor, shadowColor)
            shadowDx = getDimension(R.styleable.IndicatorConstraintLayout_shadowDx, shadowDx)
            shadowDy = getDimension(R.styleable.IndicatorConstraintLayout_shadowDy, shadowDy)
            shadowRadius = getDimension(R.styleable.IndicatorConstraintLayout_shadowRadius, shadowRadius)
            shapePadding = getDimension(R.styleable.IndicatorConstraintLayout_shape_padding, shapePadding)
            shapePaddingTop =
                getDimension(R.styleable.IndicatorConstraintLayout_shape_paddingTop, shapePadding)
            shapePaddingStart =
                getDimension(R.styleable.IndicatorConstraintLayout_shape_paddingStart, shapePadding)
            shapePaddingEnd =
                getDimension(R.styleable.IndicatorConstraintLayout_shape_paddingEnd, shapePadding)
            shapePaddingBottom =
                getDimension(R.styleable.IndicatorConstraintLayout_shape_paddingBottom, shapePadding)
            shapePaddingTop += paddingTop
            shapePaddingStart += paddingStart
            shapePaddingEnd += paddingEnd
            shapePaddingBottom += paddingBottom
            when (indicatorAttachTo) {
                Companion.top -> {
                    shapePaddingTop -= indicatorHeight
                }

                start -> {
                    shapePaddingStart -= indicatorWidth
                }

                end -> {
                    shapePaddingEnd -= indicatorWidth
                }

                Companion.bottom -> {
                    shapePaddingBottom -= indicatorHeight
                }
            }
            if (indicatorOffset < 0f) indicatorOffset = 0f
        }
        typedArray.recycle()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        isInvalidate = true
    }

    override fun invalidate() {
        super.invalidate()
        isInvalidate = true
    }

    /**
     * 绘制顺序：从左上角开始
     *   -->
     *  ↑   ↓
     *   <--
     */
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (isInvalidate) {
            invalidateCount = drawCount
            isInvalidate = false
        }
        if (invalidateCount > 0) {
//            if(!isInEditMode) XLog.i("-----> invalidateDraw!!!!!!!!!!!!!!!!!!!!")
            //初始化画笔
            paint.apply {
                color = indicatorColor
                //抗锯齿
                isAntiAlias = true
                //消抖动
                isDither = true
                //填充或镂空
                style = if (indicatorStyle == fill) Paint.Style.FILL else Paint.Style.STROKE
                //镂空画笔（边框）粗细
                if (borderWidth != 0f) strokeWidth = borderWidth
            }
            //形状宽高
            shapeWidth = width - shapePaddingEnd - shapePaddingStart
            shapeHeight = height - shapePaddingBottom - shapePaddingTop
            //圆角大小限制
            borderRadiusLimit = if (shapeWidth > shapeHeight) shapeHeight / 2 else shapeWidth / 2
            //防止刷新后有残留路径
            path.reset()
            //如果有设置listener则 把path给外部画
            if (onDrawListener != null) onDrawListener?.invoke(path, this)
            else path.apply {
                moveTo(
                    borderRadiusTopStart + shapePaddingStart + if (indicatorAttachTo == start) indicatorWidth else 0f,
                    shapePaddingTop + if (indicatorAttachTo == Companion.top) indicatorHeight else 0f
                )

                //指示器在Top
                if (indicatorWidth > 0f && indicatorHeight > 0f && indicatorAttachTo == Companion.top) {
                    drawIndicator(this, Companion.top)
                }
                //右上圆角
                lineTo(
                    shapePaddingStart + shapeWidth - borderRadiusTopEnd - (if (indicatorAttachTo == end) indicatorWidth else 0f),
                    shapePaddingTop + (if (indicatorAttachTo == Companion.top) indicatorHeight else 0f)
                )
                if (borderRadiusTopEnd != 0f) drawArcByPath(
                    this@apply, borderRadiusTopEnd,
                    shapePaddingStart + shapeWidth - borderRadiusTopEnd - if (indicatorAttachTo == end) indicatorWidth else 0f,
                    shapePaddingTop + if (indicatorAttachTo == Companion.top) indicatorHeight else 0f,
                    shapePaddingStart + shapeWidth - if (indicatorAttachTo == end) indicatorWidth else 0f,
                    shapePaddingTop + (if (indicatorAttachTo == Companion.top) indicatorHeight else 0f) + borderRadiusTopEnd,
                )

                //指示器在End
                if (indicatorWidth > 0f && indicatorHeight > 0f && indicatorAttachTo == end) {
                    drawIndicator(this, end)
                }
                //右下圆角
                lineTo(
                    shapePaddingStart + shapeWidth - if (indicatorAttachTo == end) indicatorWidth else 0f,
                    shapePaddingTop + shapeHeight - borderRadiusBottomEnd - if (indicatorAttachTo == Companion.bottom) indicatorHeight else 0f
                )
                if (borderRadiusBottomEnd != 0f) drawArcByPath(
                    this@apply, borderRadiusBottomEnd,
                    shapePaddingStart + shapeWidth - if (indicatorAttachTo == end) indicatorWidth else 0f,
                    shapePaddingTop + shapeHeight - borderRadiusBottomEnd - (if (indicatorAttachTo == Companion.bottom) indicatorHeight else 0f),
                    shapePaddingStart + shapeWidth - (if (indicatorAttachTo == end) indicatorWidth else 0f) - borderRadiusBottomEnd,
                    shapePaddingTop + shapeHeight - if (indicatorAttachTo == Companion.bottom) indicatorHeight else 0f
                )

                //指示器在Bottom
                if (indicatorWidth > 0f && indicatorHeight > 0f && indicatorAttachTo == Companion.bottom) {
                    drawIndicator(this, Companion.bottom)
                }
                //左下圆角
                lineTo(
                    shapePaddingStart + borderRadiusBottomStart + if (indicatorAttachTo == start) indicatorWidth else 0f,
                    shapePaddingTop + shapeHeight - if (indicatorAttachTo == Companion.bottom) indicatorHeight else 0f
                )
                if (borderRadiusBottomStart != 0f) drawArcByPath(
                    this@apply, borderRadiusBottomStart,
                    shapePaddingStart + borderRadiusBottomStart + if (indicatorAttachTo == start) indicatorWidth else 0f,
                    shapePaddingTop + shapeHeight - if (indicatorAttachTo == Companion.bottom) indicatorHeight else 0f,
                    shapePaddingStart + if (indicatorAttachTo == start) indicatorWidth else 0f,
                    shapePaddingTop + shapeHeight - borderRadiusBottomStart - if (indicatorAttachTo == Companion.bottom) indicatorHeight else 0f
                )

                //指示器在Start
                if (indicatorWidth > 0f && indicatorHeight > 0f && indicatorAttachTo == start) {
                    drawIndicator(this, start)
                }
                //左上圆角
                lineTo(
                    shapePaddingStart + if (indicatorAttachTo == start) indicatorWidth else 0f,
                    shapePaddingTop + (if (indicatorAttachTo == Companion.top) indicatorHeight else 0f) + borderRadiusTopStart
                )
                if (borderRadiusTopStart != 0f) drawArcByPath(
                    this@apply, borderRadiusTopStart,
                    shapePaddingStart + if (indicatorAttachTo == start) indicatorWidth else 0f,
                    shapePaddingTop + (if (indicatorAttachTo == Companion.top) indicatorHeight else 0f) + borderRadiusTopStart,
                    shapePaddingStart + (if (indicatorAttachTo == start) indicatorWidth else 0f) + borderRadiusTopStart,
                    shapePaddingTop + if (indicatorAttachTo == Companion.top) indicatorHeight else 0f
                )
                //防止path不闭合
                moveTo(
                    borderRadiusTopStart + shapePaddingStart + if (indicatorAttachTo == start) indicatorWidth else 0f,
                    shapePaddingTop + if (indicatorAttachTo == Companion.top) indicatorHeight else 0f
                )
                close()
            }
            invalidateCount--
        }
        //绘制图形
        @Suppress("DEPRECATION")
        canvas.apply {
            //绘制阴影
            save()
            /**
             * 注：clipOutPath(path) 和 clipPath(path) 的RequiresApi是26(O),但是实际生效版本是28(P)!!!
             * 因此在26、27上仍然只能使用clipPath(path, Region.Op)才可生效(DeprecatedApi:26)
             */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) clipOutPath(path) else clipPath(
                path,
                Region.Op.DIFFERENCE
            )
            paint.setShadowLayer(shadowRadius, shadowDx, shadowDy, shadowColor)
            drawPath(path, paint)
            restore()

            //绘制形状本体
            save()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) clipPath(path) else clipPath(
                path,
                Region.Op.INTERSECT
            )
            paint.clearShadowLayer()
            drawPath(path, paint)

            restore()
        }
    }

    /**
     * 绘制指示器部分
     */
    private fun drawIndicator(path: Path, attachTo: Int) {
        //如果有listener则将path给外部画
        if (onDrawIndicatorListener != null) onDrawIndicatorListener?.invoke(
            path,
            this@IndicatorConstraintLayout
        )
        else path.apply {
            when (attachTo) {
                //绘制在上边的 ^ 形状
                Companion.top -> {
                    lineTo(
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeWidth - indicatorMarginEnd - indicatorWidth
                        else (shapeWidth - indicatorWidth) * indicatorOffset) + shapePaddingStart,
                        indicatorHeight + shapePaddingTop
                    )
                    lineTo(
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeWidth - indicatorMarginEnd - indicatorWidth
                        else (shapeWidth - indicatorWidth) * indicatorOffset) + shapePaddingStart + indicatorWidth / 2,
                        shapePaddingTop
                    )
                    lineTo(
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeWidth - indicatorMarginEnd - indicatorWidth
                        else (shapeWidth - indicatorWidth) * indicatorOffset) + shapePaddingStart + indicatorWidth,
                        indicatorHeight + shapePaddingTop
                    )
                }
                //绘制在右边的 ^ 形状
                end -> {
                    lineTo(
                        shapeWidth - indicatorWidth + shapePaddingStart,
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeHeight - indicatorMarginEnd - indicatorHeight
                        else (shapeHeight - indicatorHeight) * indicatorOffset) + shapePaddingTop
                    )
                    lineTo(
                        shapeWidth + shapePaddingStart,
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeHeight - indicatorMarginEnd - indicatorHeight
                        else (shapeHeight - indicatorHeight) * indicatorOffset) + shapePaddingTop + indicatorHeight / 2
                    )
                    lineTo(
                        shapeWidth - indicatorWidth + shapePaddingStart,
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeHeight - indicatorMarginEnd - indicatorHeight
                        else (shapeHeight - indicatorHeight) * indicatorOffset) + shapePaddingTop + indicatorHeight
                    )
                }
                //绘制在下边的 ^ 形状
                Companion.bottom -> {
                    lineTo(
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeWidth - indicatorMarginEnd - indicatorWidth
                        else (shapeWidth - indicatorWidth) * indicatorOffset) + shapePaddingStart + indicatorWidth,
                        shapeHeight - indicatorHeight + shapePaddingTop
                    )
                    lineTo(
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeWidth - indicatorMarginEnd - indicatorWidth
                        else (shapeWidth - indicatorWidth) * indicatorOffset) + shapePaddingStart + indicatorWidth / 2,
                        shapeHeight + shapePaddingTop
                    )
                    lineTo(
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeWidth - indicatorMarginEnd - indicatorWidth
                        else (shapeWidth - indicatorWidth) * indicatorOffset) + shapePaddingStart,
                        shapeHeight - indicatorHeight + shapePaddingTop
                    )
                }
                //绘制在左边的 ^ 形状
                start -> {
                    lineTo(
                        shapePaddingStart + indicatorWidth,
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeHeight - indicatorMarginEnd - indicatorHeight
                        else (shapeHeight - indicatorHeight) * indicatorOffset) + shapePaddingTop + indicatorHeight
                    )
                    lineTo(
                        shapePaddingStart,
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeHeight - indicatorMarginEnd - indicatorHeight
                        else (shapeHeight - indicatorHeight) * indicatorOffset) + shapePaddingTop + indicatorHeight / 2
                    )
                    lineTo(
                        shapePaddingStart + indicatorWidth,
                        (if (indicatorMarginStart != 0f) indicatorMarginStart
                        else if (indicatorMarginEnd != 0f) shapeHeight - indicatorMarginEnd - indicatorHeight
                        else (shapeHeight - indicatorHeight) * indicatorOffset) + shapePaddingTop
                    )
                }
            }
        }
    }

    /**
     * 以顺时针顺序绘制贝塞尔曲线正圆弧
     */
    private fun drawArcByPath(
        path: Path,
        radius: Float,
        startX: Float,
        startY: Float,
        endX: Float,
        endY: Float
    ) {
//        if(!isInEditMode)XLog.d("----->path drawArcByPath: sX:$startX sY:$startY  eX:$endX eY:$endY  r:$radius")
        //控制点与数据点距离
        val d = (radius * 4 * (kotlin.math.sqrt(2.0) - 1) / 3).toFloat()
        path.apply {
            //90->0
            if (endX > startX && endY > startY)
                cubicTo(startX + d, startY, endX, endY - d, endX, endY)
            //0->270
            else if (endX < startX && endY > startY)
                cubicTo(startX, startY + d, endX + d, endY, endX, endY)
            //270->180
            else if (endX < startX && endY < startY)
                cubicTo(startX - d, startY, endX, endY + d, endX, endY)
            //180->90
            else if (endX > startX && endY < startY)
                cubicTo(startX, startY - d, endX - d, endY, endX, endY)
        }
    }

}